package microcredit.gui.editor.loan;

import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyEditorSupport;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.Date;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JSpinner.NumberEditor;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.JTextComponent;

import microcredit.gui.util.FormatUtilities;
import microcredit.gui.util.MDatePicker;
import microcredit.gui.util.NumericTextField;
import microcredit.gui.util.UIUtil;
import microcredit.model.MemberToLoan;
import microcredit.model.MemberToLoanHistory;
import microcredit.model.bo.BOLoan;
import microcredit.model.util.ModelUtils;
import net.miginfocom.swing.MigLayout;

import com.jidesoft.swing.TitledSeparator;

public class LoanPanel extends JPanel {
	private JLabel amountLabel;
	private NumericTextField amountTextField;
	private JLabel interestLabel;
	private NumericTextField interestTextField;
	private JLabel percentLabel;
	private MDatePicker startDateChooser;
	private JLabel startDateLabel;
	private JLabel weeksLabel;
	private JSpinner weeksSpinner;
	private JLabel amountValueLabel;
	private JLabel startDateValueLabel;
	private JLabel interestValueLabel;
	private JLabel weeksValueLabel;
	
	private final LoanGeneralForm form;
	private boolean editable = true;
	private JLabel repaymentRateLabel;
	private NumericTextField repaymentRateTextField;
	private JLabel repaymentRateValueLabel;
	private JLabel repaymentStartLabel;
	private MDatePicker repaymentStartDatePicker;
	private JLabel repaymentStartDateValueLabel;
	private TitledSeparator titledSeparator;
	
	public LoanPanel(LoanGeneralForm form) {
		this.form = form;
		
		initialize();
		initListeners();
		
		UIUtil.setMandatoryFields(this, MemberToLoan.LOAN_AMOUNT, 
				MemberToLoan.NUMBER_OF_WEEKS, MemberToLoan.SECURITY1, MemberToLoan.START_DATE);
	}

	private void initListeners(){
		ActionListener changeActionListener = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				form.dataChanged();
			}
		};
		
		
		PropertyChangeListener datePropertyChange = new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent e) {
				if ("date".equals(e.getPropertyName())) {
					form.dataChanged();
				}
			}
		};
		getStartDateChooser().addActionListener(changeActionListener);
		getStartDateChooser().addPropertyChangeListener(datePropertyChange);
		
		getRepaymentStartDatePicker().addActionListener(changeActionListener);
		getRepaymentStartDatePicker().addPropertyChangeListener(datePropertyChange);
		
		DocumentListener dataChangedDocumentListener = new DocumentListener() {
			@Override
			public void removeUpdate(DocumentEvent e) {
				form.dataChanged();
			}
			@Override
			public void insertUpdate(DocumentEvent e) {
				form.dataChanged();
			}
			@Override
			public void changedUpdate(DocumentEvent e) {
				form.dataChanged();
			}
		};
		
		getAmountTextField().getDocument().addDocumentListener(dataChangedDocumentListener);
		getInterestTextField().getDocument().addDocumentListener(dataChangedDocumentListener);
		getRepaymentRateTextField().getDocument().addDocumentListener(dataChangedDocumentListener);
		
		getWeeksSpinner().addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				form.dataChanged();
			}
		});
	}
	
	public void apply(MemberToLoan loan, MemberToLoanHistory history) {
		updateData(loan, history);
		updateState(loan, history);
		
		revalidate();
		repaint();
	}
	
	private void updateData(MemberToLoan loan, MemberToLoanHistory history) {
		int weeks = history.getNewNumberOfWeeks() == null ? 0 : history.getNewNumberOfWeeks();
		BigDecimal loanAmount = history.getLoanAmount();
		BigDecimal repaymentRate = history.getRepaymentRate();
		BigDecimal interest = history.getInterestPercentage();
		
		Date startDate = loan.getStartDate();
		Date repaymentStartDate = loan.getRepaymentStartDate();
		
		Long loanNumber = loan.getSequenceID();
		
		if (loanNumber == null) {
			loanNumber = BOLoan.create().generateSequenceID(loan.getMember());
		}
		if (loanNumber != null) {
			loanNumber = loanNumber+1;
		}
		
		getStartDateChooser().setDate(startDate);
		getRepaymentStartDatePicker().setDate(repaymentStartDate);
		
		getWeeksSpinner().setValue(weeks);
		getWeeksSpinner().setEnabled(repaymentRate == null);
		if (interest == null)  {
			updateNumericTextField(getInterestTextField(), new BigDecimal(0));
		} else {
			updateNumericTextField(getInterestTextField(), interest);
		}
		
		updateNumericTextField(getAmountTextField(), loanAmount);
		updateNumericTextField(getRepaymentRateTextField(), repaymentRate);
		
		getStartDateValueLabel().setText(FormatUtilities.formatDate(startDate));
		getRepaymentRateValueLabel().setText(FormatUtilities.formatMoney(repaymentRate));
		getRepaymentStartDateValueLabel().setText(FormatUtilities.formatDate(repaymentStartDate));
		getAmountValueLabel().setText(FormatUtilities.formatMoney(loanAmount));
		getInterestValueLabel().setText(FormatUtilities.formatMoney(interest)+" %");
		getWeeksValueLabel().setText(weeks+"");
		
	}
	
	static void updateNumericTextField(NumericTextField textField, BigDecimal amount) {
		Number oldAmount = null;
		try {
			oldAmount = textField.getNumberValue();
		} catch (ParseException e) {
		}
		if (!ModelUtils.equals(oldAmount, amount)) {
			try {
				textField.setText(FormatUtilities.formatMoney(amount));
			} catch (IllegalStateException e) {
				// bad practice, but ignore this error
			}
		}
	}
	
	static void updateNumericTextField(NumericTextField textField, Long amount) {
		Number oldAmount = null;
		try {
			oldAmount = textField.getNumberValue();
		} catch (ParseException e) {
		}
		if (!ModelUtils.equals(oldAmount, amount)) {
			try {
				textField.setText(String.valueOf(amount));
			} catch (IllegalStateException e) {
				// bad practice, but ignore this error
			}
		}
	}
	
	
	public MemberToLoan receive(MemberToLoan loan, MemberToLoanHistory history) {
		if (isEditable()) {
			Integer weeks = (Integer)getWeeksSpinner().getValue();
			BigDecimal loanAmount = null;
			try {
				loanAmount = (BigDecimal)getAmountTextField().getNumberValue();
			} catch (ParseException e) {
			}
			BigDecimal repaymentRate = null;
			try {
				repaymentRate = (BigDecimal)getRepaymentRateTextField().getNumberValue();
			} catch (ParseException e) {
			}
			
			BigDecimal interest = null;
			try {
				interest = (BigDecimal)getInterestTextField().getNumberValue();
			} catch (ParseException e) {
			}
			Date repaymentStartDate = getRepaymentStartDatePicker().getDate();
			Date startDate = getStartDateChooser().getDate();

			if (weeks != null) {
				history.setNewNumberOfWeeks(weeks);
				history.setRepaymentRate(repaymentRate);
			}
			history.setLoanAmount(loanAmount);
			history.setInterestPercentage(interest);
			history.setRepaymentRate(repaymentRate);
			
			loan.setStartDate(startDate);
			loan.setRepaymentStartDate(repaymentStartDate);
		}
		return loan;
	}
	
	private void updateState(MemberToLoan loan, MemberToLoanHistory history) {
		boolean extension = loan.hasExtension();
		
		setEditable(!extension);
	}
	


	
	public void setEditable(boolean editable) {
		getAmountValueLabel().setVisible(!editable);
		getInterestValueLabel().setVisible(!editable);
		getStartDateValueLabel().setVisible(!editable);
		getWeeksValueLabel().setVisible(!editable);
		getRepaymentStartDateValueLabel().setVisible(!editable);
		getRepaymentRateValueLabel().setVisible(!editable);
		
		getAmountTextField().setVisible(editable);
		getInterestTextField().setVisible(editable);
		getPercentLabel().setVisible(editable);
		getStartDateChooser().setVisible(editable);
		getWeeksSpinner().setVisible(editable);
		getRepaymentRateTextField().setVisible(editable);
		getRepaymentStartDatePicker().setVisible(editable);
	}
	
	
	public boolean isEditable() {
		return editable;
	}
	
	private void initialize() {
		setLayout(new MigLayout("insets 0, hidemode 3", "[][right][grow][right][grow][grow]", "[][grow][][][][][]"));
		add(getTitledSeparator(), "cell 1 1 5 1,grow");
		add(getAmountLabel(), "cell 1 3");
		add(getAmountTextField(), "flowx,cell 2 3");
		add(getInterestLabel(), "cell 3 3");
		add(getInterestTextField(), "flowx,cell 4 3");
		add(getStartDateLabel(), "cell 1 4");
		add(getStartDateChooser(), "flowx,cell 2 4");
		add(getWeeksLabel(), "cell 3 4");
		add(getWeeksSpinner(), "flowx,cell 4 4");
		add(getAmountValueLabel(), "cell 2 3");
		add(getPercentLabel(), "cell 4 3");
		add(getInterestValueLabel(), "cell 4 3");
		add(getStartDateValueLabel(), "cell 2 4");
		add(getWeeksValueLabel(), "cell 4 4");
		add(getRepaymentStartLabel(), "cell 1 5");
		add(getRepaymentStartDatePicker(), "flowx,cell 2 5");
		add(getRepaymentRateLabel(), "cell 3 5,alignx trailing");
		add(getRepaymentRateTextField(), "flowx,cell 4 5,growx");
		add(getRepaymentRateValueLabel(), "cell 4 5");
		add(getRepaymentStartDateValueLabel(), "cell 2 5");
	}
	private JLabel getAmountLabel() {
		if (amountLabel == null) {
			amountLabel = new JLabel("Loan:");
		}
		return amountLabel;
	}
	
	private NumericTextField getAmountTextField() {
		if (amountTextField == null) {
			amountTextField = new NumericTextField();
			amountTextField.setColumns(10);
			amountTextField.setName(MemberToLoan.LOAN_AMOUNT);
		}
		return amountTextField;
	}
	
	private JLabel getInterestLabel() {
		if (interestLabel == null) {
			interestLabel = new JLabel("Interest:");
		}
		return interestLabel;
	}
	
	private NumericTextField getInterestTextField() {
		if (interestTextField == null) {
			interestTextField = new NumericTextField();
			interestTextField.setColumns(3);
			interestTextField.setName(MemberToLoan.INTEREST_PERCENTAGE);
		}
		return interestTextField;
	}
	
	private JLabel getPercentLabel() {
		if (percentLabel == null) {
			percentLabel = new JLabel("%");
		}
		return percentLabel;
	}
	
	private MDatePicker getStartDateChooser() {
		if (startDateChooser == null) {
			startDateChooser = new MDatePicker();
			startDateChooser.setName(MemberToLoan.START_DATE);
		}
		return startDateChooser;
	}
	
	private JLabel getStartDateLabel() {
		if (startDateLabel == null) {
			startDateLabel = new JLabel("Start date:");
		}
		return startDateLabel;
	}
	
	private JLabel getWeeksLabel() {
		if (weeksLabel == null) {
			weeksLabel = new JLabel("No of weeks:");
		}
		return weeksLabel;
	}
	
	private JSpinner getWeeksSpinner() {
		if (weeksSpinner == null) {
			weeksSpinner = new JSpinner();
			weeksSpinner.setModel(new SpinnerNumberModel(0, 0, 200, 1));
			weeksSpinner.setName(MemberToLoan.NUMBER_OF_WEEKS);
			((JSpinner.NumberEditor)weeksSpinner.getEditor()).getTextField().addFocusListener(new FocusListener() {
				@Override
				public void focusLost(FocusEvent e) {
				}
				
				@Override
				public void focusGained(FocusEvent e) {
					int value = (Integer)getWeeksSpinner().getValue();
					if (value == 0) {
						((JSpinner.NumberEditor)getWeeksSpinner().getEditor()).getTextField().setText("");
					}
				}
			});
		}
		return weeksSpinner;
	}
	private JLabel getAmountValueLabel() {
		if (amountValueLabel == null) {
			amountValueLabel = new JLabel("100");
		}
		return amountValueLabel;
	}
	private JLabel getStartDateValueLabel() {
		if (startDateValueLabel == null) {
			startDateValueLabel = new JLabel("25 December 2010");
		}
		return startDateValueLabel;
	}
	private JLabel getInterestValueLabel() {
		if (interestValueLabel == null) {
			interestValueLabel = new JLabel("5%");
		}
		return interestValueLabel;
	}
	private JLabel getWeeksValueLabel() {
		if (weeksValueLabel == null) {
			weeksValueLabel = new JLabel("3");
		}
		return weeksValueLabel;
	}

	public void focusDefault() {
		getAmountTextField().requestFocus();
		getAmountTextField().selectAll();
	}
	private JLabel getRepaymentRateLabel() {
		if (repaymentRateLabel == null) {
			repaymentRateLabel = new JLabel("Custom rate:");
		}
		return repaymentRateLabel;
	}
	private NumericTextField getRepaymentRateTextField() {
		if (repaymentRateTextField == null) {
			repaymentRateTextField = new NumericTextField();
			repaymentRateTextField.setName(MemberToLoan.REPAYMENT_RATE);
			repaymentRateTextField.setColumns(10);
		}
		return repaymentRateTextField;
	}
	private JLabel getRepaymentRateValueLabel() {
		if (repaymentRateValueLabel == null) {
			repaymentRateValueLabel = new JLabel("100.00");
		}
		return repaymentRateValueLabel;
	}
	private JLabel getRepaymentStartLabel() {
		if (repaymentStartLabel == null) {
			repaymentStartLabel = new JLabel("Repay start:");
		}
		return repaymentStartLabel;
	}
	private MDatePicker getRepaymentStartDatePicker() {
		if (repaymentStartDatePicker == null) {
			repaymentStartDatePicker = new MDatePicker();
			repaymentStartDatePicker.setName(MemberToLoan.REPAYMENT_DATE);
		}
		return repaymentStartDatePicker;
	}
	private JLabel getRepaymentStartDateValueLabel() {
		if (repaymentStartDateValueLabel == null) {
			repaymentStartDateValueLabel = new JLabel("26 December 2010");
		}
		return repaymentStartDateValueLabel;
	}
	private TitledSeparator getTitledSeparator() {
		if (titledSeparator == null) {
			titledSeparator = new TitledSeparator();
			JLabel label = new JLabel("Base data");
			label.setFont(label.getFont().deriveFont(Font.BOLD));
			titledSeparator.setLabelComponent(label);
		}
		return titledSeparator;
	}
}
